#pragma once

#include <QtNetwork/QTcpSocket>
#include <boostext/iostreams/bin_stream.hpp>

#include <engine.debugger/duplex_queue.hpp>
#include <engine.debugger/Commands.hpp>
#include <engine.debugger/Responses.hpp>
#include <engine.debugger/Controller.hpp>

#include "SocketStreamDevice.hpp"




namespace Engine
{
	namespace Edit
	{
		/**
		 * This class actually takes care of connecting with a target using
		 * a tcp socket for communication.
		 */
		class DebuggerConnection
			: public QObject
		{
			Q_OBJECT

		public:

			SHARED(DebuggerConnection);

			typedef Engine::Debugger::Message      Message;
			typedef Engine::Debugger::Command      Command;
			typedef Engine::Debugger::Response     Response;
			typedef Engine::Debugger::Controller   Controller;

			typedef Engine::Debugger::duplex_queue<Message>               duplex_queue;
			typedef boostext::iostreams::bin_stream<SocketStreamDevice>   iostream;


			DebuggerConnection(QString host, quint16 port);
			virtual ~DebuggerConnection();



			/**
			 * Tests if the connection is established or not.
			 */
			bool isConnected() const;

			/**
			 * Disconnects from the target.
			 */
			void disconnect();



			/**
			 * @sends the given command to the daemon
			 */
			template <typename Command>
			services::future<typename Command::result_type> execute(const Command& command, boost::posix_time::time_duration timeout = boost::posix_time::seconds(10))
			{
				verify(m_controller != nullptr);
				return m_controller->execute(command, timeout);
			}

		protected slots:

			/**
			 * Must be called regularly to keep the debugger running.
			 */
			void run();

			/**
			 * There's an error with the socket.
			 */
			void onSocketError(QAbstractSocket::SocketError error);

			/**
			 * This slot is called when the tcp socket has connected with the game.
			 */
			void onConnected();

			/**
			 * This slot is called when the tcp socket disconnects from the game.
			 */
			void onDisconnected();

		signals:

			/**
			 * This slot is called when the socket has been disconnected
			 */
			void disconnected();

			/**
			 * This slot is called when the connection has been lost because
			 * of an error.
			 */
			void connection_error(QAbstractSocket::SocketError error);

		private:

			QTimer                        m_timer;

			QTcpSocket                    m_socket;

			// This is the stream that uses the socket as a device
			boost::shared_ptr<iostream>   m_stream;

			// This is the queue implementation that actually serializes
			// messages and forwards them to the iostream, defined above
			duplex_queue::ptr             m_queue;

			// And the actual controller that works with the queue to send commands to the daemon
			Controller::ptr               m_controller;
		};
		///////////////////////////////////////////////////////////////////////////////////////////
	}
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////
